package decorator

// 假设我们有一个咖啡店订单系统，其中咖啡是基础组件，而调料（如牛奶、摩卡、糖等）是装饰器。
// 我们需要计算不同咖啡加上不同调料的总价格，并且能够动态地添加调料。

// 在装饰器模式中一般都需要有一个装饰器基类
// 装饰器模式通常包含一个组件接口、具体组件、装饰器基类和具体装饰器。
// 装饰器基类的作用是维护对组件对象的引用，并实现组件接口，这样具体装饰器可以继承基类，只需覆盖或扩展特定方法。
// 如果去掉基类，每个具体装饰器都需要直接实现组件接口并维护组件引用，这可能导致代码重复，尤其是在多个装饰器有共同逻辑时。

// CoffeeComponent 咖啡组件接口
type CoffeeComponent interface {
	// Cost 计算咖啡价格
	Cost() float64
	// Description 描述咖啡
	Description() string
}

// BlackCoffee 黑咖啡
type BlackCoffee struct {
}
func (b *BlackCoffee) Cost() float64 {
	return 3.0
}

func (b *BlackCoffee) Description() string {
	return "black coffee"
}

// Espresso 浓缩咖啡
type Espresso struct {
}
func (e *Espresso) Cost() float64 {
	return 4.0
}

func (e *Espresso) Description() string {
	return "espresso"
}

// MilkDecorator 牛奶装饰器
type MilkDecorator struct {
	// 桥接关键：持有组件引用
	component CoffeeComponent
}

func NewMilkDecorator(component CoffeeComponent) *MilkDecorator {
	return &MilkDecorator{
		component: component,
	}
}

// Cost 计算牛奶装饰后的价格
func (m *MilkDecorator) Cost() float64 {
	return m.component.Cost() + 1.0
}

// Description 描述牛奶装饰后的咖啡
func (m *MilkDecorator) Description() string {
	return m.component.Description() + " with milk"
}

// SugarDecorator 糖装饰器
type SugarDecorator struct {
	// 桥接关键：持有组件引用
	component CoffeeComponent
}

func NewSugarDecorator(component CoffeeComponent) *SugarDecorator {
	return &SugarDecorator{
		component: component,
	}
}

// Cost 计算糖装饰后的价格
func (s *SugarDecorator) Cost() float64 {
	return s.component.Cost() + 0.5
}

// Description 描述糖装饰后的咖啡
func (s *SugarDecorator) Description() string {
	return s.component.Description() + " with sugar"
}

// CreamDecorator 奶油装饰器
type CreamDecorator struct {
	// 桥接关键：持有组件引用
	component CoffeeComponent
}

func NewCreamDecorator(component CoffeeComponent) *CreamDecorator {
	return &CreamDecorator{
		component: component,
	}
}

// Cost 计算奶油装饰后的价格
func (c *CreamDecorator) Cost() float64 {
	return c.component.Cost() + 0.7
}

// Description 描述奶油装饰后的咖啡
func (c *CreamDecorator) Description() string {
	return c.component.Description() + " with cream"
}
